module share.edcode;

import std.c.string : memcpy;


public struct DefaultMessage
{
    public int Recog;
    public ushort Ident;
    public ushort Param;
    public ushort Tag;
    public ushort Series;
}


int Encode6BitBuf(in char* pszSrc, char* pszDest, int nSrcLen, int nDestLen)
{
	int nDestPos = 0;
	int nRestCount	= 0;
	ubyte ch, chMade = 0, chRest = 0;

	for (int i = 0; i < nSrcLen; i++)
	{
		if (nDestPos >= nDestLen) break;
		ch = pszSrc[i];
		
		chMade = cast(ubyte)((chRest | (ch >> (2 + nRestCount))) & 0x3f);
		chRest = cast(ubyte)(((ch << (8 - (2 + nRestCount))) >> 2) & 0x3f);

		nRestCount += 2;

		if (nRestCount < 6)
			pszDest[nDestPos++] = cast(ubyte)(chMade + 0x3c);
		else
		{
			if (nDestPos < nDestLen - 1)
			{
				pszDest[nDestPos++]	= cast(ubyte)(chMade + 0x3c);
				pszDest[nDestPos++]	= cast(ubyte)(chRest + 0x3c);
			}
			else
				pszDest[nDestPos++] = cast(ubyte)(chMade + 0x3c);

			nRestCount	= 0;
			chRest		= 0;
		}
	}

	if (nRestCount > 0)
		pszDest[nDestPos++] = cast(ubyte)(chRest + 0x3c);

	pszDest[nDestPos] = 0;

	return nDestPos;
}

int Decode6BitBuf(in char* pszSrc, char* pszDest, int nSrcLen, int nDestLen)
{
	static immutable ubyte[5] Decode6BitMask = [ 0xfc, 0xf8, 0xf0, 0xe0, 0xc0 ];
	
	int	nDestPos = 0, nBitPos = 2;
	int	nMadeBit = 0;
	ubyte ch = 0, chCode, tmp;

	for (int i = 0; i < nSrcLen; i++)
	{
		if ((pszSrc[i] - 0x3c) >= 0)
			ch = cast(ubyte)(pszSrc[i] - 0x3c);
		else
		{
			nDestPos = 0;
			break;
		}

		if (nDestPos >= nDestLen) break;

		if ((nMadeBit + 6) >= 8)
		{
			chCode = cast(ubyte)(tmp | ((ch & 0x3f) >> (6 - nBitPos)));
			pszDest[nDestPos++] = chCode;

			nMadeBit = 0;

			if (nBitPos < 6) 
				nBitPos += 2;
			else
			{
				nBitPos = 2;
				continue;
			}
		}

		tmp = ((ch << nBitPos) & Decode6BitMask[nBitPos - 2]);

		nMadeBit += (8 - nBitPos);
	}

	//pszDest[nDestPos] = 0;

	return nDestPos;
}


void MakeDefMessage(out DefaultMessage defMsg, ushort wIdent, int nRecog = 0, ushort wParam = 0, ushort wTag = 0, ushort wSeries = 0)
{
    defMsg.Ident = wIdent;
    defMsg.Recog = nRecog;
    defMsg.Param = wParam;
    defMsg.Tag = wTag;
    defMsg.Series = wSeries;
}

unittest
{
    DefaultMessage msg = void;
    MakeDefMessage(msg, 1000, 1, 2, 3, 4);
    
    assert (msg.Ident == 1000, "[MakeDefaultMsg] value Ident not correct.");
    assert (msg.Recog == 1, "[MakeDefaultMsg] value Recog not correct.");
    assert (msg.Param == 2, "[MakeDefaultMsg] value Param not correct.");
    assert (msg.Tag == 3, "[MakeDefaultMsg] value Tag not correct.");
    assert (msg.Series == 4, "[MakeDefaultMsg] value Series not correct.");
}

string EncodeString(in string source)
{
    static char[1024] buffer = void;

    int len = Encode6BitBuf(source.ptr, &buffer[0], source.length, buffer.sizeof);
    buffer[len] = 0;
    return buffer[0 .. len].idup;
}

int EncodeString(in string source, char[] buffer)
{
    return Encode6BitBuf(source.ptr, &buffer[0], source.length, buffer.length);
}

string DecodeString(in string source)
{
    static char[1024] buffer = void;

    int len = Decode6BitBuf(source.ptr, &buffer[0], source.length, buffer.sizeof);
    buffer[len] = 0;
    return buffer[0 .. len].idup;
}

int DecodeString(in string source, char[] buffer)
{
    return Decode6BitBuf(source.ptr, &buffer[0], source.length, buffer.sizeof);
}

unittest
{
    string s1 = "Username";
    string s2, s3;
    
    s2 = EncodeString( s1 );
    s3 = DecodeString( s2 );
    
    assert (s2 == "QSIaXbu]WRP", "[EncodeString] failed.");
    assert (s3 == "Username", "[DecodeString] failed.");
}

int EncodeMessage(in DefaultMessage defMsg, char[] buffer)
{
    static char[32] bytes = void;
    
    memcpy(bytes, cast(void*)&defMsg, defMsg.sizeof);
    return Encode6BitBuf(&bytes[0], &buffer[0], defMsg.sizeof, buffer.length);
}

int DecodeMessage(out DefaultMessage defMsg, char[] buffer)
{
    return Decode6BitBuf(&buffer[0], cast(char*)&defMsg, buffer.length, defMsg.sizeof);
}

/*string DecodeMessage(DefaultMessage defMsg)
{
    static char[64] buffer = void;
    
    char[] sbytes = cast(char[])(cast(char*)(&defMsg))[0..defMsg.sizeof];
    int len = Decode6BitBuf(sbytes, buffer[], sbytes.length, buffer.sizeof);
    return buffer[0 .. len].idup;
}*/

unittest
{
    char[32] buffer;
    DefaultMessage msg1, msg2;

    MakeDefMessage(msg1, 1000, 1, 2, 3, 4);
    EncodeMessage(msg1, buffer);
    DecodeMessage(msg2, buffer);

    //"<L<<<J\?<\<?<<L<"
    assert (true, "[EncodeMessage] values are not correct. ");

    bool ok = (msg2.Ident == 1000) && (msg2.Recog == 1) && (msg2.Param == 2) &&
        (msg2.Tag == 3) && (msg2.Series == 4);
    assert (ok, "[DecodeMessage] values are not correct.");
}

int EncodeBuffer(in void* sourceptr, int ptrsize, char[] buffer)
{
    return Encode6BitBuf(cast(char*)sourceptr, &buffer[0], ptrsize, buffer.length);
}

int DecodeBuffer(in char[] source, void* destptr, int ptrsize)
{
    return Decode6BitBuf(&source[0], cast(char*)destptr, source.length, ptrsize);
}

unittest
{
    char[32] buffer;
    int number1, number2;

    number1 = 25;
    EncodeBuffer(&number1, number1.sizeof, buffer);
    DecodeBuffer(buffer, &number2, number2.sizeof);
    
    //"<L<<<J\?<\<?<<L<"
    assert (true, "[EncodeBuffer] value is not correct. ");
    assert (number2 == 25, "[DecodeBuffer] value is not correct.");
}